package com.citic.netty.client.bootstrap;

import java.util.Date;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

import com.citic.netty.client.console.ConsoleCommandManager;
import com.citic.netty.client.console.LoginConsoleCommand;
import com.citic.netty.client.handler.CreateGroupResponseHandler;
import com.citic.netty.client.handler.GroupMessageResponseHandler;
import com.citic.netty.client.handler.HeartBeatTimerHandler;
import com.citic.netty.client.handler.JoinGroupResponseHandler;
import com.citic.netty.client.handler.ListGroupMembersResponseHandler;
import com.citic.netty.client.handler.LoginResponseHandler;
import com.citic.netty.client.handler.LogoutResponseHandler;
import com.citic.netty.client.handler.MessageResponseHandler;
import com.citic.netty.client.handler.QuitGroupResponseHandler;
import com.citic.netty.common.codec.LengthFieldBasedFrameDecoderUnpacker;
import com.citic.netty.common.codec.PacketDecoder;
import com.citic.netty.common.codec.PacketEncoder;
import com.citic.netty.common.handler.IMIdleStateHandler;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class BootStrapClient {
	
	private static final int MAX_RETRY = 5;
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 8000;

    public static boolean isLogin = false;
    
    public static void main(String[] args) {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();
        bootstrap
	        .group(workerGroup)
	        .channel(NioSocketChannel.class)
	        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
	        .option(ChannelOption.SO_KEEPALIVE, true)
	        .option(ChannelOption.TCP_NODELAY, true)
	        .handler(new ChannelInitializer<SocketChannel>() {
	            @Override
	            public void initChannel(SocketChannel ch) {
	            	ch.pipeline().addLast(new IMIdleStateHandler());
	            	ch.pipeline().addLast(new LengthFieldBasedFrameDecoderUnpacker());
	                ch.pipeline().addLast(new PacketDecoder());
                    ch.pipeline().addLast(new LoginResponseHandler());
                    ch.pipeline().addLast(new MessageResponseHandler());
                    ch.pipeline().addLast(new CreateGroupResponseHandler());
                    ch.pipeline().addLast(new GroupMessageResponseHandler());
                    ch.pipeline().addLast(new JoinGroupResponseHandler());
                    ch.pipeline().addLast(new QuitGroupResponseHandler());
                    ch.pipeline().addLast(new ListGroupMembersResponseHandler());
                    ch.pipeline().addLast(new LogoutResponseHandler());
                    ch.pipeline().addLast(new PacketEncoder());
                    ch.pipeline().addLast(new HeartBeatTimerHandler());
	            }
	        });

        connect(bootstrap, HOST, PORT, MAX_RETRY);
    }

    private static void connect(Bootstrap bootstrap, String host, int port, int retry) {
        bootstrap.connect(host, port).addListener(future -> {
            if (future.isSuccess()) {
            	Channel channel = ((ChannelFuture) future).channel();
                startConsoleThread(channel);
            } else if (retry == 0) {
            	System.err.println("重试次数已用完，放弃连接！");
            } else {
                int order = (MAX_RETRY - retry) + 1;			// 第几次重连
                int delay = 1 << order;							// 本次重连的间隔
                System.err.println(new Date() + ": 连接失败，第" + order + "次重连……");
                bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retry - 1), delay, TimeUnit.SECONDS);
            }
        });
    }

	private static void startConsoleThread(Channel channel) {
		ConsoleCommandManager consoleCommandManager = new ConsoleCommandManager();
        LoginConsoleCommand loginConsoleCommand = new LoginConsoleCommand();
        Scanner scanner = new Scanner(System.in);
        
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (!Thread.interrupted()) {
					if (!isLogin) {
	                    loginConsoleCommand.exec(scanner, channel);
	                } else {
	                    consoleCommandManager.exec(scanner, channel);
	                }
				}
			}
		}).start();
	}
}
